# -*- coding: utf-8 -*-

from openerp.osv import fields
from openerp.osv import osv
from openerp import SUPERUSER_ID
from openerp import api
from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT
from openerp.tools.translate import _
from datetime import datetime
from datetime import timedelta
import openerp.addons.decimal_precision as dp
import pytz,time

ISODATEFORMAT = '%Y-%m-%d'
ISODATETIMEFORMAT = "%Y-%m-%d %H:%M:%S"

class order_type(osv.osv):
    _name = "order.type"
    _rec_name = 'order_type_code'

    def name_get(self, cr, uid, ids, context=None):
        """ 因为本张表的 _rec_name 是 一个selection 字段 ,如果没有这个
        name_get 则这张表的代表字段就是 selection的 key 很容易让人迷惑 所以加了name_get更直接的表现出销售订单类型"""
        res = []
        for order_type in self.browse(cr, uid, ids, context=context):
            res.append((order_type.id, dict(self._fields['order_type_code'].selection).get(order_type.order_type_code)))
        return res

    _columns = {
        'order_type_code': fields.selection([('normal_order', u'普通订单'), ('card_order', u'卡券订单'), ('shop_order', u'商城预付订单'),
                                             ('card_exchange_order', u'卡券兑换订单'), ('point_order', u'积分订单'),
                                             ('sample_order', u'样品订单'), ('shop_dispatch_order', u'商城发货订单'),
                                             ('customer_gift_order', u'客户送礼订单')], string=u"订单类型"),
        'stock_location_id': fields.many2one('stock.location', u'计算库存库位', required=True),
        'stock_picking_type_id': fields.many2one('stock.picking.type', string=u'入库操作类型', required=True)
    }


class card_model(osv.osv):
    """" 卡券模型"""
    _name = 'card.model'

    _columns = {
        'name': fields.char(u'名称'),
        'sale_order_ids': fields.one2many('sale.order', 'card_model_id', string=u'销售订单列表'),
        'product_ids': fields.one2many('card.model.product', 'card_tmpl_id', string=u'产品列表'),
        'is_multiple_choice_one':fields.boolean(u'是否多选一'),
        'note':fields.text(u'备注')
    }

class sale_order_card_product(osv.osv):
    _name='sale.order.card.product'
    _rec_name = 'product_id'
    _order = 'order_id desc,suite,id'
    def name_get(self,cr,uid,ids,context=None):
        result = []
        for sale_order_card_product in self.browse(cr, uid, ids, context):
            result.append((sale_order_card_product.id,"%s(%s)"%(sale_order_card_product.product_id.name,sale_order_card_product.product_qty)))
        return result

    def onchange_product_qty(self, cr, uid, ids, product_qty, context=None):
        if product_qty < 1:
            return {
                'value': {
                    'product_qty': 1,},
                'warning': {'title': u'错误', 'message': u'产品数量不能小于1!'}
            }
    #预计未提货数(6,9,10
    def _compute_future_exchange_qty(self, cr, uid, ids, name, args, context=None):
        res = {}
        # model_data_obj = self.pool.get('ir.model.data')
        # card_exchange_order_id = model_data_obj.xmlid_to_res_id(
        #     cr, SUPERUSER_ID, 'dftg_ext.dftg_order_type_data_card_exchange_order')
        # order_type_exchange = self.pool.get('order.type').browse(cr, uid, card_exchange_order_id, context=context)
        #取得所有卡券订单ids
        ids_list_string = '(%s)' % ','.join(map(str, ids))  # 用list替代tuple,避免sql问题
        sql = "SELECT order_id FROM sale_order_card_product WHERE id in %s" % ids_list_string
        cr.execute(sql)
        order_id_rows = cr.dictfetchall()
        order_ids = []
        for order_id_row in order_id_rows:
            order_ids.append(order_id_row.get('order_id'))

        order_ids = list(set(order_ids))
        for sale_order in self.pool.get('sale.order').browse(cr, uid, order_ids, context=context):
            socp_product_ids_map = {}
            #总的已提货份数
            exchange_sum = 0
            virtual_exchange_sum = 0
            suite_exchange_count_map = {} #选项提货数map（防止相同选项重复相加）
            suite_exchange_virtual_number_map = {} #选项提货比率计算计算基数map(把0提货数量变成可计算比率的数字，比如+1)
            suite_exchange_rate_map = {} #选项提货比率
            have_zero_exchange_suite = False
            for socp_row in sale_order.sale_order_card_product_ids:
                card_exchange_count = socp_row.card_exchange_count
                suite_exchange_count_map.update({socp_row.suite: card_exchange_count / socp_row.product_qty})
                socp_product_ids_map.update({socp_row.id: socp_row.product_id.id})
                if card_exchange_count == 0:
                    have_zero_exchange_suite = True
                suite_exchange_virtual_number_map.update({socp_row.suite: card_exchange_count / socp_row.product_qty})

            #虚拟提货数如果有0的提货数量，则都加上1
            if have_zero_exchange_suite:
                virtual_exchange_sum = 0
                for suite in suite_exchange_virtual_number_map:
                    suite_exchange_virtual_number_map.update({suite: suite_exchange_virtual_number_map.get(suite) + 1})

            #计算总提货份数， 和虚拟提货总份数（用于计算提货比率）
            for suite in suite_exchange_count_map:
                exchange_sum += suite_exchange_count_map.get(suite)
                virtual_exchange_sum += suite_exchange_virtual_number_map.get(suite)

            #剩余提货份数
            exchange_residue = sale_order.card_qty - exchange_sum

            #计算比率
            for suite in suite_exchange_count_map:
                suite_exchange_rate_map.update({suite: 1.0 * suite_exchange_virtual_number_map.get(suite) / virtual_exchange_sum})


            for socp_row in sale_order.sale_order_card_product_ids:
                #当前选项提货比率
                suite_rate = suite_exchange_rate_map.get(socp_row.suite)

                #按比率预估未提货数
                if socp_row.id not in res:
                    res[socp_row.id] = 0

                res[socp_row.id] = round(exchange_residue * suite_rate * socp_row.product_qty, 2)
        return res

    # 采购未入库数
    def _compute_purchase_incoming_qty(self, cr, uid, ids, name, args, context=None):
        res = {}
        model_data_obj = self.pool.get('ir.model.data')
        po_line_obj = self.pool.get('purchase.order.line')
        card_exchange_order_id = model_data_obj.xmlid_to_res_id(
            cr, SUPERUSER_ID, 'dftg_ext.dftg_order_type_data_card_exchange_order')
        order_type_exchange = self.pool.get('order.type').browse(cr, uid, card_exchange_order_id, context=context)
        #取得所有卡券订单ids
        ids_list_string = '(%s)' % ','.join(map(str, ids))  # 用list替代tuple,避免sql问题
        sql = "SELECT order_id FROM sale_order_card_product WHERE id in %s" % ids_list_string
        cr.execute(sql)
        order_id_rows = cr.dictfetchall()
        order_ids = []
        for order_id_row in order_id_rows:
            order_ids.append(order_id_row.get('order_id'))

        order_ids = list(set(order_ids))
        for sale_order in self.pool.get('sale.order').browse(cr, uid, order_ids, context=context):
            for socp_row in sale_order.sale_order_card_product_ids:

                # 已经发货 还没有 提货的 份数
                po_line_ids = po_line_obj.search(cr,uid,[('product_id','=',socp_row.product_id.id),
                                                                    ('order_id.location_id','=',order_type_exchange.stock_location_id.id)],context=context)

                #采购未入库数(未考虑退货的情况)
                res[socp_row.id] = sum([po_line_row.product_qty - po_line_row.incomed_qty
                                                                 for po_line_row in po_line_obj.browse(cr,uid,po_line_ids,context=context) if po_line_row.product_qty>0])


    # 计算已发货产品数量(1,4
    def _compute_shipped_count(self, cr, uid, ids, name, args, context=None):
        res = {}
        ids_list_string = '(%s)' % ','.join(map(str, ids)) #用list替代tuple,避免sql问题
        cr.execute("""SELECT socp.id, sum(sol.product_uom_qty) AS order_products_sum
            FROM apply_delivery ap
                INNER JOIN sale_order so ON so.apply_delivery_id=ap.id
                INNER JOIN sale_order_card_product socp ON ap.card_so_id=socp.order_id
                INNER JOIN sale_order_line sol ON sol.order_id = so.id AND sol.product_id = socp.product_id
            WHERE ap.is_shipping=TRUE AND socp.id IN %s
            GROUP BY socp.id, ap.card_so_id, socp.product_id, ap.is_shipping ORDER BY socp.id"""
                   % ids_list_string)
        reserved_no_sent_rows = cr.dictfetchall()
        for id in ids:
            res[id] = 0
        for reserved_no_sent_row in reserved_no_sent_rows:
            socp_id = reserved_no_sent_row.get('id')
            res[socp_id] = reserved_no_sent_row.get('order_products_sum')
        return res


    #根据提货订单取得对应卡券订单的产品列表ids
    def _get_sale_order_card_products(self, cr, uid, ids, context=None):
        apply_delivery_ids = []
        for sale_order_row in self.pool.get('sale.order').browse(cr, uid, ids, context=context):
            apply_delivery_ids.append(sale_order_row.apply_delivery_id.id)
        return apply_delivery_ids

    # 计算已锁未发产品数量(2,5
    def _compute_reserved_no_sent_count(self, cr, uid, ids, name, args, context=None):
        res = {}
        ids_list_string = '(%s)' % ','.join(map(str, ids)) #用list替代tuple,避免sql问题
        cr.execute("""SELECT socp.id, sum(sol.product_uom_qty) AS order_products_sum
             FROM apply_delivery ap
                 INNER JOIN sale_order so ON so.apply_delivery_id=ap.id
                 INNER JOIN sale_order_card_product socp ON ap.card_so_id=socp.order_id
                 INNER JOIN sale_order_line sol ON sol.order_id = so.id AND sol.product_id = socp.product_id
             WHERE so.is_stock_reserved=TRUE AND ap.is_shipping=FALSE AND socp.id IN %s
             GROUP BY socp.id, ap.card_so_id, ap.suite, socp.product_id ORDER BY id""" % ids_list_string)
        reserved_no_sent_rows = cr.dictfetchall()
        for id in ids:
            res[id] = 0
        for reserved_no_sent_row in reserved_no_sent_rows:
            socp_id = reserved_no_sent_row.get('id')
            res[socp_id] = reserved_no_sent_row.get('order_products_sum')
        #
        #
        # res = {}
        # for apply_delivery_row in self.pool.get('apply.delivery').browse(cr,uid,ids,context=context):
        #     suite = apply_delivery_row.suite
        #     if not suite:
        #         suite = 1
        #     is_stock_reserved = apply_delivery_row.is_stock_reserved
        #     is_shipping = apply_delivery_row.is_shipping
        #     card_product_ids = self.search(cr, uid, [('order_id', '=', apply_delivery_row.card_so_id.id), ('suite', '=', suite)], context=context)
        #     for card_product_row in self.browse(cr, uid, card_product_ids, context=context):
        #         # 如果已保留并且未发货，则+1
        #         if is_stock_reserved and not is_shipping:
        #             res[card_product_row.id] = card_product_row.reserved_no_sent_count + 1
        #             print '---------- : + 1'
        #         else:
        #             res[card_product_row.id] = card_product_row.reserved_no_sent_count - 1
        #             print '---------- : - 1'
        return res

    # 计算可用库存(5,7,8
    def _get_change_stock_quant(self,cr,uid,ids, name, args, context=None):
        res = {}
        model_data_obj = self.pool.get('ir.model.data')
        card_exchange_order_id = model_data_obj.xmlid_to_res_id(
            cr, SUPERUSER_ID, 'dftg_ext.dftg_order_type_data_card_exchange_order')
        order_type_exchange = self.pool.get('order.type').browse(cr, uid, card_exchange_order_id, context=context)
        card_location_id = order_type_exchange.stock_location_id.id
        child_card_location_ids = self.pool.get('stock.location')\
            .get_child_location_ids(cr, uid, card_location_id, context=context)
        #库存默认值设为0
        for id in ids:
            res[id] = 0
        #设置产品库存
        if isinstance(child_card_location_ids, int):
            child_card_location_ids = [child_card_location_ids]
        for socp_row in self.browse(cr, uid, ids, context=context):
            cr.execute("""SELECT product_id, sum(qty) as stock_qty FROM stock_quant
                        WHERE reservation_id IS NULL AND location_id IN %s
                        GROUP BY product_id""" % str(child_card_location_ids).replace('[', '(').replace(']', ')'))
            product_quant_rows = cr.dictfetchall()
            product_stock_qty = {}
            for product_quant_row in product_quant_rows:
                product_stock_qty.setdefault(product_quant_row.get('product_id'), product_quant_row.get('stock_qty'))
            product_id = socp_row.product_id.id
            if product_stock_qty.has_key(product_id):
                res[socp_row.id] = product_stock_qty.get(product_id)
        return res
    
    def _get_locked_stock_quant(self, cr, uid, ids, name, args, context=None):
        res = {}
        model_data_obj = self.pool.get('ir.model.data')
        card_exchange_order_id = model_data_obj.xmlid_to_res_id(
            cr, SUPERUSER_ID, 'dftg_ext.dftg_order_type_data_card_exchange_order')
        order_type_exchange = self.pool.get('order.type').browse(cr, uid, card_exchange_order_id, context=context)
        card_location_id = order_type_exchange.stock_location_id.id
        child_card_location_ids = self.pool.get('stock.location') \
            .get_child_location_ids(cr, uid, card_location_id, context=context)
        # 库存默认值设为0
        for id in ids:
            res[id] = 0
        # 设置产品库存
        for socp_row in self.browse(cr, uid, ids, context=context):
            cr.execute("SELECT product_id, sum(qty) as stock_qty FROM stock_quant"
                       " WHERE reservation_id=-1 AND so_id=%s AND location_id IN %s"
                       " GROUP BY product_id", (socp_row.order_id.id, tuple(child_card_location_ids)))
            product_quant_rows = cr.dictfetchall()
            product_stock_qty = {}
            for product_quant_row in product_quant_rows:
                product_stock_qty.setdefault(product_quant_row.get('product_id'), product_quant_row.get('stock_qty'))
            product_id = socp_row.product_id.id
            if product_stock_qty.has_key(product_id):
                res[socp_row.id] = product_stock_qty.get(product_id)
        return res
    
    # 计算最小采购量(产品已提货数 - 已发货数 - 已锁未发 - 采购未入库数 - 可用库存)(3
    @api.depends('sent_count', 'reserved_no_sent_count', 'purchase_incoming_qty', 'future_exchange_qty', 'stock_qty')
    def _compute_purchase_qty(self, cr, uid, ids, name, args, context=None):
        res = {}
        card_product_rows = self.browse(cr, uid, ids, context=context)
        for card_product_row in card_product_rows:
            plan_purchase_qty = card_product_row.future_exchange_qty \
                - card_product_row.purchase_incoming_qty \
                - card_product_row.stock_qty
            if plan_purchase_qty < 0:
                plan_purchase_qty = 0
                #已提货未发货数
                no_send_count = card_product_row.card_exchange_count - card_product_row.sent_count - card_product_row.reserved_no_sent_count
                #库存剩余数
                stock_residue_qty = round(card_product_row.stock_qty - no_send_count - card_product_row.future_exchange_qty)
                if stock_residue_qty > 0:
                    plan_purchase_qty = 0 - stock_residue_qty
            res[card_product_row.id] = plan_purchase_qty
        return res

    def view_product_bom_list(self, cr, uid, ids, context=None):
        wizard_obj = self.pool.get('show.message.wizard')
        for product_bom in self.browse(cr, uid, ids, context=context):
            model_data_obj = self.pool.get('ir.model.data')
            stock_location_id = model_data_obj.xmlid_to_res_id( cr, SUPERUSER_ID, 'dftg_ext.stock_location_card_exchange')
            cr.execute("""SELECT
                              inpp.default_code  AS code,
                              inpp.name_template AS name,
                              mbl.product_qty    AS qty,
                              insq.qty           AS stock_qty
                            FROM mrp_bom mp
                              INNER JOIN mrp_bom_line mbl ON mbl.bom_id = mp.id
                              INNER JOIN product_product inpp ON inpp.id = mbl.product_id
                              LEFT JOIN (SELECT
                                           sum(sq.qty)     AS qty,
                                           sq.product_id   AS product_id
                                         FROM stock_quant sq
                                         WHERE sq.location_id = %s AND sq.reservation_id IS NULL 
                                         GROUP BY sq.product_id) insq ON insq.product_id = mbl.product_id
                            WHERE mp.product_tmpl_id IN (SELECT pp.product_tmpl_id
                                                         FROM product_product AS pp
                                                         WHERE pp.id = %s);
                           """ %(stock_location_id, product_bom.product_id.id))
            fetchall_dict = cr.dictfetchall()
            bom_message_list = ["[%s]%s * %s 库存 %s" % (line.get('code'), line.get('name'), line.get('qty'), line.get('stock_qty'))
                            for line in fetchall_dict]
            message_id = wizard_obj.create(cr, uid, {'message': '\n'.join(bom_message_list)}, context=context)
            return {
                'type': 'ir.actions.act_window',
                'res_model': 'show.message.wizard',
                'view_mode': 'form',
                'view_type': 'form',
                'res_id': message_id,
                'target': 'new'
            }

    def onchange_product_id(self, cr, uid, ids, product_id, context=None):
        if product_id:
            cr.execute("""
                          SELECT * FROM mrp_bom mp WHERE mp.product_tmpl_id in 
                          (SELECT pp.product_tmpl_id FROM product_product as pp WHERE pp.id=%s)
                          """ % str(product_id))
            fetchall_dict = cr.fetchall()
            if fetchall_dict and fetchall_dict[0]:
                return {'value': {'have_bom': True}}
            else:
                return {'value': {'have_bom': False}}
        else:
            return {'value': {'have_bom': False}}
    
    _columns = {
        'order_id': fields.many2one('sale.order', string='销售订单'),
        'have_bom': fields.boolean(string='是否有bom'),
        'express_type_id': fields.many2one('express.type', string='快递方式'),
        'product_id': fields.many2one('product.product', string='产品'),
        'product_qty': fields.float(u'单份提货数量', digits_compute=dp.get_precision('Product Unit of Measure'),
                                    readonly=True,  copy=False),
        'product_price': fields.float(u'产品价格',  copy=False),
        'suite': fields.selection([(1, u'选项1'), (2, u'选项2'), (3, u'选项3'), (4, u'选项4'),(5, u'选项5'), (6, u'选项6'),
                                   (7, u'选项7'),(8, u'选项8'), (9, u'选项9'), (10, u'选项10')], string='套装'),
        'cost_price': fields.float(u'成本价',  copy=False),
        'card_exchange_count': fields.integer(u'产品已提货数', readonly=True),
        'purchase_incoming_qty': fields.function(_compute_purchase_incoming_qty, string=u'采购未入库',type='integer'),
        'future_exchange_qty': fields.function(_compute_future_exchange_qty, string=u'预计未提货数', type='float'),
        'sent_count': fields.function(_compute_shipped_count, type='integer', string=u'已发货数',
                                      readonly=True, copy=False),
        'reserved_no_sent_count': fields.function(_compute_reserved_no_sent_count, type='integer',
                                                  string=u'已锁未发数', readonly=True),
        # 'reserved_no_sent_count': fields.function(_compute_reserved_no_sent_count, type='integer', string=u'已锁未发数',
        #                                          readonly=True,
        #                                          store={
        #                                              'apply.delivery': (lambda self, cr, uid, ids, c={'watch_field': 'is_shipping'}: ids, ['is_shipping'], 40),
        #                                              'sale.order': (_get_sale_order_card_products, ['is_stock_reserved'], 40),
        #                                          }
        #                                         ),

        'stock_qty':fields.function(_get_change_stock_quant, string=u'可用库存',type='integer'),
        'locked_qty':fields.function(_get_locked_stock_quant, string=u'已锁库存',type='integer'),
        'need_purchase_qty': fields.function(_compute_purchase_qty, string=u'剩余采购数', type='integer', ),
        'card_qty':fields.related('order_id', 'card_qty', type='integer', string='计划总份数', readonly=True),
        'is_enabled': fields.boolean(u'是否启用'),
        'reserved_quantity': fields.float(u'预留数量'),
        'product_outer_name': fields.char(u'给客户看的名称！'),
        'suite_name': fields.char(u'套装名称（仓库发货）'),
    }
    _defaults = {
        'have_bom': False,
        'suite': 1,
        'product_qty': 1,
        'is_enabled': True,
    }


class show_message_wizard(osv.osv_memory):
    _name = 'show.message.wizard'
    _columns = {
        'message': fields.text(u'查看信息')
    }
    
    def close(self, cr, uid, ids, context=None):
        return True
    
class sale_order_line(osv.osv):
    _inherit = 'sale.order.line'
    _columns = {
        'card_exchange_id': fields.many2one('apply.delivery', u'提货记录id'),
    }

    # 建立订单行的时候把订单的卡券兑换id复制到订单行上(如果有的话)
    def create(self, cr, uid, vals, context=None):
        order_id = vals.get('order_id')
        order_row = self.pool.get('sale.order').browse(cr, uid, order_id, context=context)
        if order_row.apply_delivery_id:
            vals.setdefault('card_exchange_id', order_row.apply_delivery_id.id)
        return_vals = super(sale_order_line, self).create(cr, uid, vals, context=context)
        return return_vals

class card_model_product(osv.osv):
    """" 卡券模型列表"""
    _name = 'card.model.product'

    def onchange_card_product_id(self, cr, uid, ids, product_id, context=None):
        """冗余产品名字 以便于修改"""
        if product_id:
            product_objs = self.pool.get('product.product').browse(cr, uid, product_id, context=context)
            return {
                'value': {
                    'product_name': product_objs.name}
            }
    def name_get(self,cr,uid,ids,context=None):
        result = []
        for card_model_product in self.browse(cr, uid, ids, context):
            result.append((card_model_product.id,"%s%s"%(card_model_product.product_name,card_model_product.product_qty)))
        return result

    def onchange_card_product_qty(self, cr, uid, ids, product_qty, context=None):
        """产品数量不能小与1,不然这一行记录没有意义!"""
        if product_qty <= 0:
            return {
                'value': {
                    'product_qty': 1, },
                'warning': {'title': u'错误', 'message': u'产品数量不能小于1!'}
            }
    _columns = {
        'product_id': fields.many2one('product.product', u'产品'),
        'product_name': fields.char(u'产品名称'),
        'product_qty': fields.integer(u'数量',digits_compute=dp.get_precision('Product Unit of Measure')),
        'card_tmpl_id': fields.many2one('card.model', u'主表关联')
    }
    _defaults = {
        'product_qty': 1,
    }


class product_category(osv.osv):
    _inherit = 'product.category'
    _columns = {
        'code': fields.char(u'编号'),
    }


class apply_delivery(osv.osv):
    _inherit = 'apply.delivery'

    def unlink(self, cr, uid, ids, context=None):
        for apply in self.browse(cr, uid, ids, context=context):
            if not apply.is_canceled:
                raise osv.except_osv(u'错误', u"券号%s对应提货记录未取消不能删除!" % apply.copy_code)
        return super(apply_delivery, self).unlink(cr, uid, ids, context=context)

    def _compute_product_list(self, cr, uid, ids, name, args, context=None):
        res = {}
        for apply_delivery  in self.browse(cr,uid,ids,context=context):
            res[apply_delivery.id] = self.get_product_list(cr,uid,apply_delivery.code.id,context=context)
        return res

    def cancel_exchange_apply_delivery(self, cr, uid, ids, context=None):
        sale_order_obj = self.pool.get('sale.order')
        for ad_record in self.browse(cr, uid, ids, context=context):
            so_id = ad_record.card_so_id.id
            self.exchange_suite(cr, uid, so_id, ad_record.suite, type="subtraction", context=context)
            self.exchange_suite(cr, uid, so_id, ad_record.orign_apply_delivery_id.suite, type="add",
                                context=context)
            ad_record.write({'is_canceled': True})
            if ad_record.is_shipping:
                raise osv.except_osv(u'错误', u"提货记录已发货，不可取消提货记录!")
            if ad_record.is_export and uid != SUPERUSER_ID:
                raise osv.except_osv(u'错误', u"提货记录已导出, 不可取消提货记录!")
            for line in ad_record.so_id.order_line:
                if line.shipped_qty > 0:
                    raise osv.except_osv(_('Warning!'),
                                         u'需要取消取消提货订单（出库单没有移动的话），否则报错“提货订单已发货，不能取消提货记录!')
            if ad_record.is_export and uid != SUPERUSER_ID:
                raise osv.except_osv(_('Warning!'), u'记录已导出，不能取消！如有疑问，请联系管理员!')
            if ad_record.so_id.state != 'draft':
                sale_order_obj.apply_change_action(cr, SUPERUSER_ID, [ad_record.so_id.id], context=context)
            if ad_record.so_id.state != 'cancel':
                sale_order_obj.action_cancel(cr, SUPERUSER_ID, [ad_record.so_id.id], context=context)
        return True

    def get_product_list(self, cr, uid, card_id, context=None):
        card_line = self.pool.get('product.card.line').browse(cr, SUPERUSER_ID, card_id,
                                                              context=context)
        model_data_obj = self.pool.get('ir.model.data')
        card_exchange_order_id = model_data_obj.xmlid_to_res_id(
            cr, SUPERUSER_ID, 'dftg_ext.dftg_order_type_data_card_exchange_order')
        order_type_exchange = self.pool.get('order.type').browse(cr,uid,card_exchange_order_id,context=context)
        location_ids = self.pool.get('stock.location').get_child_location_ids(cr,uid,order_type_exchange.stock_location_id.id,context=context)
        result_dict = self.pool.get(
            'product.product').get_product_location_available_qty(cr, uid, [product.product_id.id for product in
                                                                            card_line.so_id.sale_order_card_product_ids],
                                                                  so_id=card_line.so_id.id,
                                                                  context=context)
        product_html_list = []
        note = card_line.so_id.note or ''
        for product in card_line.so_id.sale_order_card_product_ids:
            stock_qty_or_disabled = int(sum([result_dict.get((product.product_id.id, None, location_id), 0)
                                 for location_id in location_ids]))
            if not product.is_enabled:
                stock_qty_or_disabled = '<span style="color:red">禁</span>'
            elif stock_qty_or_disabled == 0:
                stock_qty_or_disabled = '<span style="color:red">无</span>'
            elif stock_qty_or_disabled > 0:
                stock_qty_or_disabled = '<span style="color:blue">%s</span>' % stock_qty_or_disabled
            product_html_list.append(
                """<tr><td class='oe_list_field_cell oe_list_field_selection'>%s</td> <td
                class='oe_list_field_cell oe_list_field_selection'>%s</td><td class='oe_list_field_cell
                oe_list_field_selection'> %s</td><td class='oe_list_field_cell
                oe_list_field_selection'>%s</td></tr>""" % (
                    "[%s]%s" % (product.product_id.default_code, str(product.product_id.name)),
                    int(product.product_qty), stock_qty_or_disabled, product.suite))
        area_limit_prompt = ''
        area_limit = card_line.so_id.origin
        if area_limit:
            area_limit_prompt = '<span style="color:red">地区限制：' + area_limit + '</span>, '
        return area_limit_prompt + """<span style="color:#5566ee">%s</span><table class='oe_list_content'><tr><td class='oe_list_field_cell oe_list_field_selection'>产品</td> <td
                 class='oe_list_field_cell oe_list_field_selection'>数量</td><td class='oe_list_field_cell
                oe_list_field_selection'>库存</td><td class='oe_list_field_cell oe_list_field_selection'>选项</td></tr>%s<table>"""\
               % (note, ''.join(product_html_list))

    def _compute_delivery_product_list(self,cr,uid,ids,name,args,context=None):
        res = {}
        for apply_delivery_row in self.browse(cr,uid,ids,context=context):
            product_message = []
            if not apply_delivery_row.orign_apply_delivery_id:
                if apply_delivery_row.suite:
                    domain= [('suite', '=',apply_delivery_row.suite),('order_id', '=',apply_delivery_row.card_so_id.id)]
                else:
                    domain = [('order_id', '=', apply_delivery_row.card_so_id.id)]
                product_card_sale_id = self.pool.get('sale.order.card.product').search(cr, uid, domain, order='product_id', context=context)
                suite_name = ''
                for product in self.pool.get('sale.order.card.product').browse(cr, uid, product_card_sale_id,
                                                                               context=context):
                    if product.suite_name and not suite_name:
                        suite_name = u'%s × 1' % (product.suite_name)
                    product_message.append(u'%s × %s' % (product.product_id.name, int(product.product_qty)))
                if suite_name:
                    res[apply_delivery_row.id] = suite_name
                else:
                    res[apply_delivery_row.id] = ' & '.join(product_message) #顺丰电子面单打印只能打印2行,用换行会导致2行后打不出来, 用"&"符号分隔
                if apply_delivery_row.card_so_id:
                    sale_note = apply_delivery_row.card_so_id.order_line[0].sale_note
                    if sale_note:
                        res[apply_delivery_row.id] = sale_note
                if suite_name:
                    res[apply_delivery_row.id] = suite_name
                else:
                    res[apply_delivery_row.id] = ' & '.join(product_message) #顺丰电子面单打印只能打印2行,用换行会导致2行后打不出来, 用"&"符号分隔
            else:
                for line in apply_delivery_row.so_id.order_line:
                    if line.product_uom_qty > 0:
                        product_message.append(u'%s × %s' % (line.product_id.name, int(line.product_uom_qty)))
                res[apply_delivery_row.id] = ' & '.join(product_message)  # 顺丰电子面单打印只能打印2行,用换行会导致2行后打不出来, 用"&"符号分隔
                sale_note = apply_delivery_row.card_so_id.order_line[0].sale_note
                if sale_note:
                    res[apply_delivery_row.id] = sale_note
        return res

    def on_change_code_password(self, cr, uid, ids, copy_code, password, context=None):
        res = super(apply_delivery, self).on_change_code_password(cr, uid, ids, copy_code, password, context=context)
        if res.get('value'):
            if res['value'].get('code'):
                card_line = self.pool.get('product.card.line').browse(cr, SUPERUSER_ID, res['value'].get('code'),
                                                                      context=context)
                res['value'].update({'is_multiple_choice_one': card_line.so_id.is_multiple_choice_one,
                                     'partner_id': card_line.so_id.partner_id.id, 'card_so_id': card_line.so_id.id})
                res['value'].update({'product_list': self.get_product_list(cr, uid, res['value'].get('code'), context=context)})
            else:
                res['value'].update({'is_multiple_choice_one': False})
        return res

    def create_order_line_choose_suite(self,cr,uid,card_product_id,pricelist,partner_id,card_exchange_id,
                                       product_num_change=0,context=None):
        #写入已提货数量
        card_product_id.write(
            {'card_exchange_count': card_product_id.card_exchange_count + (product_num_change or card_product_id.product_qty)})
        return_vals = self.pool.get('sale.order.line').product_id_change_with_wh(cr, 1, False, pricelist,
                                                            card_product_id.product_id.id, product_num_change or card_product_id.product_qty,
                                                            False, False, False, False,  partner_id,
                                                            context.get('lang'), False, False, False, False, False,
                                                            warehouse_id=False, context=context)
        return_vals['value'].update({'product_id': card_product_id.product_id.id,
                                     'product_uom_qty': product_num_change or card_product_id.product_qty, 'price_unit': 0,
                                     'card_exchange_id': card_exchange_id})
        return return_vals['value']

    def create_normal_sale_order_line(self, cr, uid, vals, so_vals, context=None):
        so_line_obj = self.pool.get('sale.order.line')
        card_line = self.pool.get('product.card.line').browse(cr, uid, vals.get('code'), context=context)
        order_line = []
        so_vals.update({'partner_id': vals.get('partner_id')})
        if vals.get('is_multiple_choice_one') or card_line.so_id.is_multiple_choice_one:
            for card_product_id in card_line.so_id.sale_order_card_product_ids:
                if card_product_id.suite == vals.get('suite'):
                    order_line.append([0, False, self.create_order_line_choose_suite(cr, uid, card_product_id,
                                        so_vals.get('pricelist'),vals.get('partner_id'),vals.get('card_exchange_id'),context=context)])
        else:
            for card_product in card_line.so_id.sale_order_card_product_ids:
                card_product.write({'card_exchange_count': card_product.card_exchange_count + card_product.product_qty})
            # 如果是外部订单，需要根据选项生成提货订单
            if vals.get('outer_order_ref'):
                line_vals = so_line_obj.product_id_change_with_wh(cr, uid, False, so_vals.get('pricelist'),
                                                                  vals.get('product_id'),
                                                                  vals.get(u'数量'),
                                                                  False, False, False, False, vals.get('partner_id'),
                                                                  context.get('lang'), False, False, False, False,
                                                                  False,
                                                                  warehouse_id=False, context=context)
                line_vals['value'].update(
                    {'product_id': vals.get('product_id'), 'product_uom_qty': vals.get(u'数量'),
                     'price_unit': 0, 'card_exchange_id': vals.get('card_exchange_id')})
                order_line.append([0, False, line_vals['value']])
            else:
                for card_product_id in card_line.so_id.sale_order_card_product_ids:
                    line_vals = so_line_obj.product_id_change_with_wh(cr, uid, False, so_vals.get('pricelist'),
                                                                        card_product_id.product_id.id, card_product_id.product_qty,
                                                                        False, False, False, False, vals.get('partner_id'),
                                                                        context.get('lang'), False, False, False, False,
                                                                        False,
                                                                        warehouse_id=False, context=context)
                    line_vals['value'].update(
                        {'product_id': card_product_id.product_id.id, 'product_uom_qty': card_product_id.product_qty,
                         'price_unit': 0, 'card_exchange_id': vals.get('card_exchange_id')})
                    order_line.append([0, False, line_vals['value']])
        return order_line


    def exchange_suite(self, cr, uid, so_id, suite, type='add', context=None):
        product_obj = self.pool.get('sale.order.card.product')
        for card_product in product_obj.search_read(cr, uid, [('order_id', '=', so_id),\
                                                              ('suite', '=', suite)], ['id', 'card_exchange_count', 'product_qty'], context=context):
            if type == 'add':
                product_obj.write(cr, uid, card_product.get('id'), {'card_exchange_count': 
                card_product.get('card_exchange_count') + card_product.get('product_qty', 1) }, context=context)
            else:
                 product_obj.write(cr, uid, card_product.get('id'), {'card_exchange_count':
                  card_product.get('card_exchange_count') - card_product.get('product_qty', 1) }, context=context)
            
    def create_exchange_sale_order_line(self, cr, uid, vals, so_vals, context=None):
        exchange_row = self.pool.get("apply.delivery.exchange").browse(cr, uid,
                                                                       context.get('apply_delivery_exchange_id'),context=context)
        suite_new_prodcut_ids = dict([(suite_prodcut.product_id.id, suite_prodcut.product_num)
                                      for suite_prodcut in exchange_row.suit_product_new_list])
        suite_old_prodcut_ids = dict([(suite_prodcut.product_id.id, -suite_prodcut.product_num)
                                      for suite_prodcut in exchange_row.suit_product_old_list])
        order_line = []
        vals.update({'suite': exchange_row.suite_new})
        self.exchange_suite(cr, uid, exchange_row.apply_delivery_id.card_so_id.id, exchange_row.suite_new, type='add', context=context)
        order_line.extend(self.product_card_line_change_detail(cr, uid, vals, so_vals,
                                                               suite_new_prodcut_ids, context=context))
        vals.update({'suite': exchange_row.suite_old})
        self.exchange_suite(cr, uid, exchange_row.apply_delivery_id.card_so_id.id, exchange_row.suite_old, type='subtraction', context=context)
        order_line.extend(self.product_card_line_change_detail(cr, uid, vals, so_vals,
                                                               suite_old_prodcut_ids, context=context))
        return order_line

    def product_card_line_change_detail(self, cr, uid, vals, so_vals, suite_prodcut_ids, context=None):
        so_line_obj = self.pool.get('sale.order.line')
        card_line = self.pool.get('product.card.line').browse(cr, uid, vals.get('code'),
                                                              context=context)
        order_line = []
        for card_product_id, product_num_change in suite_prodcut_ids.iteritems():
            #写入已提货数量
            return_vals = so_line_obj.product_id_change_with_wh(cr, uid, False, so_vals.get('pricelist'),
                                                                card_product_id,
                                                                product_num_change,
                                                                False, False, False, False, vals.get('partner_id'),
                                                                context.get('lang'), False, False, False, False,
                                                                False,
                                                                warehouse_id=False, context=context)
            return_vals['value'].update(
                {'product_id': card_product_id, 'product_uom_qty': product_num_change,
                 'price_unit': 0, 'card_exchange_id': vals.get('card_exchange_id')})
            order_line.append([0, False, return_vals['value']])
        return order_line

    def create_sale_order(self, cr, uid, vals, context=None):
        so_obj = self.pool.get('sale.order')
        model_data_obj = self.pool.get('ir.model.data')
        so_vals = so_obj.onchange_partner_id(cr, uid, False, vals.get('partner_id'), context=context)
        card_exchange_order_id = model_data_obj.xmlid_to_res_id(
            cr, SUPERUSER_ID, 'dftg_ext.dftg_order_type_data_card_exchange_order')
        so_vals.update({'partner_id': vals.get('partner_id')})
        if not vals.get('orign_apply_delivery_id', False):
            order_line = self.create_normal_sale_order_line(cr, uid, vals, so_vals, context=context)
        else:
            order_line = self.create_exchange_sale_order_line(cr, uid,vals, so_vals, context=context)
            # 依据所选的产品进行新建换货订单
        if not order_line:
            raise osv.except_osv(_('Warning!'),u'所选套装不存在!')
        so_vals.update(
            {'consignee': vals.get('to_name'), 'shipping_type_sale': 'express_delivery',
             'phone_number': vals.get('phone_number'), 'mobile_number': vals.get('mobile_number'),
             'county_id': vals.get('county_id'), 'city_id': vals.get('city_id'),
             'province_id': vals.get('province_id'), 'street': vals.get('address'),
             'card_so_id': vals.get('card_so_id'), 'need_package': u'是',
             'zip': vals.get('postal_code'), 'order_line': order_line,
             'order_type_id': card_exchange_order_id,
             'expected_delivery_time': vals.get('estimated_ship_date'),
             'apply_delivery_id': vals.get('card_exchange_id'), 'payment_method': u'现金'
            })
        so_id = so_obj.create(cr, uid, so_vals, context=context)
        so_obj.action_button_confirm(cr, uid, [so_id], context=context)
        so_obj.create_out_going_picking(cr, uid,  so_id, context=context)
        return so_id

    def synchronous_cost_logistics(self, card_sale_order,
                                   old_delivery_cost, new_delivery_cost, context=None):
        cost_logistics = card_sale_order.cost_logistics - old_delivery_cost + new_delivery_cost
        card_sale_order.write({'cost_logistics': cost_logistics})
        return True

    def create(self, cr, uid, vals, context=None):
        card_so_id = vals.get('card_so_id')
        product_line_obj = self.pool.get('sale.order.card.product')
        card_sale_order = self.pool.get('sale.order').browse(cr, 1, card_so_id, context=context)
        product_line_ids = product_line_obj.search(cr, uid, [('suite', '=', vals.get('suite')),
                                                             ('express_type_id', '!=', False),
                                                             ('order_id', '=', card_so_id)], context=context)
        product_line_rows = product_line_obj.browse(cr, uid, product_line_ids, context=context)
        if product_line_rows or card_sale_order.card_express_type:
            vals.update({'card_express_type': product_line_rows and \
                                              product_line_rows[0].express_type_id and \
                                              product_line_rows[0].express_type_id.id \
                                              or card_sale_order.card_express_type.id})
        #暂时用关联单号来检查地区是否符合
        origin = card_sale_order.origin
        if origin and vals.get('province_id'):
            country_row = self.pool.get('res.country.state').browse(cr, uid, int(vals.get('province_id')), context=context)
            if origin.find(country_row.name) < 0:
                raise osv.except_osv(_('Warning!'), u'此卡券只在%s地区发货' % (origin,))
        apply_delivery_id = super(apply_delivery, self).create(cr, uid, vals, context=context)
        vals.update({'card_exchange_id': apply_delivery_id})#1019 销售订单行增加一个字段“提货记录id”card_exchange_id关联到apply.delivery
        # ，提货记录的产品快速搜索改为搜索销售订单行的产品 [Issue开启
        so_id = self.create_sale_order(cr, SUPERUSER_ID, vals, context=context)
        if vals.get('delivery_cost'):
            self.synchronous_cost_logistics(card_sale_order, 0, vals.get('delivery_cost'))
        self.write(cr, uid, apply_delivery_id, {'so_id': so_id}, context=context)
        return apply_delivery_id

    def cancel_apply_delivery(self, cr, uid, ids, context=None):
        sale_order_obj = self.pool.get('sale.order')
        return_vals = super(apply_delivery, self).cancel_apply_delivery(cr, uid, ids, context=context)
        for ad_record in self.browse(cr, uid, ids, context=context):
            self.back_the_apply_suite_qty(cr, uid, ad_record, context=context)
            for line in ad_record.so_id.order_line:
                if line.shipped_qty > 0:
                    raise osv.except_osv(_('Warning!'),
                                         u'需要取消取消提货订单（出库单没有移动的话），否则报错“提货订单已发货，不能取消提货记录!')
            if ad_record.is_export and uid != SUPERUSER_ID:
                raise osv.except_osv(_('Warning!'), u'记录已导出，不能取消！如有疑问，请联系管理员!')
            if ad_record.so_id.state not in ('draft', 'cancel'):
                sale_order_obj.apply_change_action(cr, SUPERUSER_ID, [ad_record.so_id.id],context=context)
            sale_order_obj.action_cancel(cr, SUPERUSER_ID, [ad_record.so_id.id],context=context)
        return return_vals

    def back_the_apply_suite_qty(self, cr, uid, apply_delivery, context=None):
        card_product_obj = self.pool.get('sale.order.card.product')
        if not apply_delivery.orign_apply_delivery_id:
            card_product_id = card_product_obj.search(cr, uid, [('order_id', '=', apply_delivery.card_so_id.id),
                                                                ('suite', '=', apply_delivery.suite)], context=context)
            product_id_qty_dict = {}
            for line in apply_delivery.so_id.order_line:
                product_id_qty_dict.update({line.product_id.id:product_id_qty_dict.get(line.product_id.id,0)+line.product_uom_qty})
            for card_product in card_product_obj.browse(cr, uid, card_product_id, context=context):
                card_product.write({'card_exchange_count': card_product.card_exchange_count - card_product.product_qty})
                if card_product.product_qty != product_id_qty_dict.get(card_product.product_id.id):
                    raise osv.except_osv(_('Warning!'),
                                         u'提货订单已经进行修改，不能请修改！')
        return True

    def write(self, cr, uid, ids, vals, context=None):
        for ad_record in self.browse(cr, uid, ids, context=context):
            if vals.get('delivery_cost'):
                self.synchronous_cost_logistics(ad_record.card_so_id, ad_record.delivery_cost,
                                                vals.get('delivery_cost'))
                if not ad_record.delivery_cost_import_date:
                    vals.update({'delivery_cost_import_date': datetime.now()})
            if vals.get('province_id'):
                self.pool.get('sale.order').apply_change_action(cr, SUPERUSER_ID, [ad_record.so_id.id], context=context)
                # 暂时用关联单号来检查地区是否符合
                origin = ad_record.card_so_id.origin
                if origin and vals.get('province_id'):
                    country_row = self.pool.get('res.country.state').browse(cr, uid, int(vals.get('province_id')),
                                                                            context=context)
                    if origin.find(country_row.name) < 0:
                        raise osv.except_osv(_('Warning!'), u'此卡券只在%s地区发货' % (origin,))
            # 如果修改了选项
            if vals.get('suite'):
                self.back_the_apply_suite_qty(cr, uid, ad_record, context=context)
                # 检查是否已经导出
                #if ad_record.is_export:
                #    raise osv.except_osv(_('Warning!'), u'此卡券仓库已经导出准备发货，无法修改选项')
                self.pool.get('sale.order').apply_change_action(cr, SUPERUSER_ID, [ad_record.so_id.id], context=context)
                self.pool.get('sale.order.line').unlink(cr,uid,[line.id for line in ad_record.so_id.order_line],context=context)
                for card_product_id in ad_record.card_so_id.sale_order_card_product_ids:
                    if card_product_id.suite == vals.get('suite'):
                        order_line_vals= self.create_order_line_choose_suite(cr,uid,card_product_id,
                                                                             False, ad_record.so_id.partner_id.id, ad_record.id,context=context)
                        order_line_vals.update({'order_id':ad_record.so_id.id})
                        self.pool.get('sale.order.line').create(cr,uid,order_line_vals,context=context)
                self.pool.get('sale.order').action_button_confirm(cr, SUPERUSER_ID, [ad_record.so_id.id],context=context)
        res = super(apply_delivery, self).write(cr, uid, ids, vals, context)
        return res

    def on_change_suite(self, cr, uid, ids, card_so_id, suite, context=None):
        values = {'is_enabled_suite': True, 'has_suite_stock': True}
        warning = {}
        if card_so_id and suite:
            domain = [('order_id', '=', card_so_id), ('suite', '=', suite)]
            card_product_ids = self.pool.get('sale.order.card.product').search(cr, uid, domain, context=context)
            if card_product_ids:
                card_product_row = self.pool.get('sale.order.card.product').browse(cr, uid, card_product_ids[0], context=context)
                if not card_product_row.is_enabled:
                    values.update({'is_enabled_suite': False})
                elif not card_product_row.stock_qty:
                    values.update({'has_suite_stock': False})
            else:
                warning = {'title': u'错误', 'message': u"无此选项！"}
        return {'value': values, 'warning': warning}

    def _compute_suite_state(self, cr, uid, ids, name, args, context=None):
        res = {}
        for id in ids:
            res[id] = True
        return res

    _columns = {
        'is_multiple_choice_one': fields.boolean(u'是否多选一',copy=False),
        'choice_product_id': fields.many2one('product.product', u'多选一产品'),
        'so_id': fields.many2one('sale.order', u'提货订单', readonly=True,copy=False),
        'card_so_id': fields.many2one('sale.order',u'卡券订单'),
        'partner_id': fields.many2one('res.partner', u'卡券所属企业', readonly=True),
        'product_list': fields.function(_compute_product_list,type='html',string=u'卡券产品明细', readonly=True,copy=False),
        'suite': fields.selection([(1, u'选项1'), (2, u'选项2'), (3, u'选项3'), (4, u'选项4'),(5, u'选项5'), (6, u'选项6'),
                                   (7, u'选项7'),(8, u'选项8'), (9, u'选项9'), (10, u'选项10')], string='套装',copy=False, track_visibility='onchange'),
        'order_line_ids':fields.one2many('sale.order.line','card_exchange_id',u'订单行产品'),
        # 'product_id':fields.related('order_line_ids','product_id',type='many2one',relation='product.product',string='提货产品')
        'delivery_product_list': fields.function(_compute_delivery_product_list, type='text',string=u'发货内容', readonly=True,
                                                 store={'apply.delivery': (lambda self, cr, uid, ids, c={}: ids, ['suite','so_id'], 11),}
                                                 ),
        'delivery_cost':fields.float(u'快递费用',digits_compute=dp.get_precision('Product Unit of Measure'),  copy=False),
        'is_stock_reserved':fields.related('so_id','is_stock_reserved',type='boolean',string='出库单已锁定',store=False,copy=False),
        'export_date': fields.datetime(u'导出时间', copy=False),
        'export_uid': fields.many2one('res.users', u'导出人', copy=False),
        'card_express_type': fields.many2one('express.type', u'要求快递方式'),
        'so_product_id': fields.related('so_id', 'product_id', type='many2one', relation="product.product", string='产品',
                                        copy=False),
        'exchange_so_id': fields.many2one('sale.order', u'换货订单', readonly=True, copy=False),
        'is_enabled_suite': fields.function(_compute_suite_state, string=u'是否启用的套件', type='boolean', readonly=True,
                                            copy=False),
        'has_suite_stock': fields.function(_compute_suite_state, string=u'是否无库存的套件', type='boolean', readonly=True,
                                           copy=False),
        'apply_exchange_delivery_ids': fields.one2many('apply.delivery.exchange', 'apply_delivery_id', string=u"换货申请",
                                                       copy=False),
        'delivery_cost_import_date': fields.datetime(u'快递费用导入时间')
    }
    _defaults = {
        'suite': 1,
        #一定要用lambda, 否则值在odoo启动时就固定了, 不会在新建时重新计算
        'estimated_ship_date': lambda self, cr, uid, context=None: (datetime.now() + timedelta(days=1)).strftime(ISODATEFORMAT),
    }


class mrp_bom(osv.osv):
    _inherit = 'mrp.bom'

    def create(self, cr, uid, vals, context=None):
        if self.search(cr, uid, [('product_tmpl_id', '=', vals.get('product_tmpl_id'))], context=context):
            raise osv.except_osv(_('Warning!'),
                                 u'此产品已经存在bom记录！')
        return_vals = super(mrp_bom, self).create(cr, uid, vals, context=context)

        bom = self.browse(cr,uid,return_vals,context=context)

        for line in bom.bom_line_ids:
            if self.search(cr,uid,[('product_tmpl_id', '=',line.product_id.product_tmpl_id.id)],context=context):
                raise osv.except_osv(_('Warning!'),
                                 u'原料的组成产品不能是有bom的记录！')
        return return_vals

    def write(self, cr, uid, ids, vals, context=None):
        if self.search(cr, uid, [('product_tmpl_id', '=', vals.get('product_tmpl_id'))], context=context):
            raise osv.except_osv(_('Warning!'),
                                 u'此产品已经存在bom记录！')
        return_vals = super(mrp_bom, self).write(cr, uid, ids, vals, context=context)
        for bom in self.browse(cr, uid, ids, context=context):
            product_ids = self.pool.get('product.product').search(cr, uid, [('product_tmpl_id', '=', bom.product_tmpl_id.id)], context=context)
            move_ids = self.pool.get('stock.move').search(cr, uid, [('product_id', 'in', product_ids),
                                                                    ('change_id', '!=', False)], context=context)
            if move_ids:
                raise osv.except_osv(_('Warning!'), u' 此产品已经生产过，不能修改BOM清单!')
            for line in bom.bom_line_ids:
                if self.search(cr,uid,[('product_tmpl_id', '=',line.product_id.product_tmpl_id.id)],context=context):
                    raise osv.except_osv(_('Warning!'),
                                     u'原料的组成产品不能是有bom的记录！')
        return return_vals

class sale_order(osv.osv):
    _inherit = 'sale.order'
    _order = 'id desc'
    _track = {
        'state': {
            'sale.mt_order_confirmed': lambda self, cr, uid, obj, ctx=None: obj.state in ['manual', 'progress'],
            'sale.mt_order_sent': lambda self, cr, uid, obj, ctx=None: obj.state in ['sent'],
            'dftg_ext.mt_order_cancel': lambda self, cr, uid, obj, ctx=None: obj.state in ['cancel']
        },
    }
    def transform_to_sale_order(self, cr, uid, ids,context=None):
        model_data_obj = self.pool.get('ir.model.data')
        normal_order_type_id = model_data_obj.xmlid_to_res_id(
            cr, SUPERUSER_ID, 'dftg_ext.dftg_order_type_data_normal_order')
        for order in self.browse(cr, uid, ids, context=context):
            res = {}
            if order.shipment_completion:
                res.update({'name': order.name[3:],
                            'is_sample': False,
                            'order_type_id': normal_order_type_id})
                order.write(res)
            else:
                raise osv.except_osv(_('Warning!'),
                                     u'样品订单还还没有完成发货，不能转换成销售订单！')
        return True

    def on_change_card_model_id(self, cr, uid, ids, card_model_id, pricelist_id, date_order, partner_id,
                                sale_order_name, parent_states, context=None):
        res, card_line_list = [], []
        sale_order_line_obj = self.pool.get('sale.order.line')
        if card_model_id:
            card_model_objs = self.pool.get('card.model').browse(cr, uid, card_model_id, context=context)[0]
            for product in card_model_objs.product_ids:
                card_line_list.append([0, False,{'product_id':product.product_id.id,'product_qty':product.product_qty,'product_price':product.product_id.list_price}])
            model_data_obj = self.pool.get('ir.model.data')
            order_type_id = model_data_obj.xmlid_to_res_id(
                cr, SUPERUSER_ID, 'dftg_ext.dftg_order_type_data_card_order')
            return {'value': {
                    'sale_order_card_product_ids':card_line_list,
                    'is_card':True,
                    'is_multiple_choice_one':card_model_objs.is_multiple_choice_one,
                    'order_type_id':order_type_id,
                    }}
        else:
            if partner_id:
                return {'value': {
                        'is_card': False,
                        'sale_order_card_product_ids': card_line_list,
                        'is_multiple_choice_one':False,
                        'order_type_id':False,
                        }}

    def onchange_is_card(self, cr, uid, ids,is_card,pricelist_id,date_order, partner_id, sale_order_name, parent_states,context=None):
        sale_order_line_obj = self.pool.get('sale.order.line')
        model_data_obj = self.pool.get('ir.model.data')
        res = []
        waring, values = {}, {}
        if is_card and partner_id:
            for card_serrvice in ['dftg_ext.dftg_product_product_card_service_ext',
                                  'dftg_ext.dftg_product_product_card_delivery_service',
                                  'dftg_ext.dftg_product_card_customer_service_expenses'
                                  ]:
                product_id = model_data_obj.xmlid_to_res_id(
                    cr, SUPERUSER_ID, card_serrvice)
                return_values = sale_order_line_obj.dftg_product_id_change_with_wh(cr, uid, ids, pricelist_id, product_id,
                                                                                   '', sale_order_name,
                                                                                   1, False, 0, False, '', partner_id,
                                                                                   False, True, date_order, False, False,
                                                                                   False, False, parent_states,
                                                                                   context=context)
                (return_values.get('value')).update({'product_id': product_id, 'product_uom_qty': 1, 'state': 'draft'})
                res.append([0, False, return_values.get('value')])
            values = {'order_line':res}
        elif is_card and not partner_id:
            waring = {'title': u'错误', 'message': u"请先选择客户！"}
            values = {'is_card':False}
        else:
            values = {'order_line': False}
        return {'value': values
            ,'warning':waring}

    def time_compare(self, cr, uid, create_date, expected_delivery_time, context=None):
        """
        在 create write 和确认是否需要采购审核的判定中都用到了期望发货日期 合理性判断
        :param create_date: 创建日期是比较的一个基数
        :param expected_delivery_time:
        :param context:在判定采购需要审核的 计算字段中调用这个方法的时候是传过来一个key为return_boolean的布尔值
        :return:当context中return_boolean为True 则已返回True 和 False 为主
        """
        week_day = {3: {2: 2, 3: 2, 4: 2, 5: 1}, 4: {1: 2, 2: 2, 3: 2, 4: 2, 5: 1}}
        # if self.pool.get('res.users').user_has_groups(cr, uid, groups='dftg_ext.allow_modify_so_expected_date',
        #                                               context=context):
        #     return True   ##整体业务逻辑判断变更，取消这个组
        if not create_date:
            date_create_order = datetime.now(pytz.timezone("Asia/Shanghai"))
        else:
            create_date_time = time.strptime(create_date, ISODATETIMEFORMAT)
            date_create_order = datetime(*create_date_time[:3],tzinfo=pytz.timezone("Asia/Shanghai"))
        if date_create_order.strftime(ISODATETIMEFORMAT) > date_create_order.strftime("%Y-%m-%d 15:00:00"):
            few_days = 4
        else:
            few_days = 3
        few_days += (week_day.get(few_days)).get(datetime.now().weekday(), 0)
        if expected_delivery_time < (date_create_order + timedelta(days=few_days)).strftime(ISODATEFORMAT):
            return True
        else:
            return False

    def on_change_expected_delivery_time(self, cr, uid, ids, expected_delivery_time, context=None):

        return {}
    def check_card_qty_equal_product_uom_qty(self,cr,uid,ids,context=None):
        for so_row in self.browse(cr,uid,ids,context=context):
            if not all([so_line_row.product_uom_qty==so_row.card_qty for so_line_row in so_row.order_line]):
                raise osv.except_osv(_('错误!'),u'产品数量跟提货份数不相等，不能保存！')
        return True

    def create(self, cr, uid, vals, context=None):
        sale_order_id = super(sale_order,self).create(cr,uid,vals,context=context)
        so_obj = self.browse(cr,uid,sale_order_id,context=context)
        if so_obj.is_card and vals.get('note'):
            raise osv.except_osv(_('错误!'), u'卡券订单请内部备注字段里写备注！')
        if so_obj.is_card and not so_obj.real_so_id:
            if not vals.get('sale_order_card_product_ids'):
                raise osv.except_osv(_('错误!'),
                                     u'请添加订单卡券产品关联后再保存！')
            for line in so_obj.order_line:
                if line.product_id.type != 'service' and not line.product_id.is_card:
                    raise osv.except_osv(_('Warning!'),
                                         u'卡券模型 相关的订单的产品不能为实物产品！')
            so_line_vals = self.create_sale_order_card_product(cr, uid, so_obj, context=context)
            vals={}
            vals['order_line'] = []
            vals['order_line'].append(so_line_vals)
            so_obj.write(vals)
            product_suite_count = []
            [product_suite_count.append(product.suite)
             for product in so_obj.sale_order_card_product_ids]
            if not so_obj.real_so_id and len(list(set(product_suite_count))) != so_obj.how_choice_one:
                raise osv.except_osv(_('Warning!'),
                                     u'套件选项和几选一的数量不一致!')
            self.check_card_qty_equal_product_uom_qty(cr, uid, sale_order_id, context=context)
        # else:  ##整体业务逻辑判断变更，取消这段功能
        #     model_data_obj = self.pool.get('ir.model.data')
        #     order_type_id = model_data_obj.xmlid_to_res_id(
        #         cr, SUPERUSER_ID, 'dftg_ext.dftg_order_type_data_card_exchange_order')
        #     compare_time = True
        #     for line in so_obj.order_line:
        #         seanson_list = [season.season for season in line.product_id.product_tmpl_id.season_ids]
        #         if len(seanson_list) != len(list(set(seanson_list) - set(['zhongqiu']))):
        #             compare_time = False
        #     if compare_time and vals.get('order_type_id') != order_type_id and vals.get('expected_delivery_time'):
        #         self.time_compare(cr, uid, False, so_obj.expected_delivery_time, context=context)
        return sale_order_id

    def write(self, cr, uid, ids, vals, context=None):
        sale_order_id = super(sale_order, self).write(cr, uid, ids, vals, context=context)
        model_data_obj = self.pool.get('ir.model.data')
        card_exchange_order_id = model_data_obj.xmlid_to_res_id(
            cr, SUPERUSER_ID, 'dftg_ext.dftg_order_type_data_card_exchange_order')
        for so_obj in self.browse(cr, uid, ids, context=context):
            if so_obj.is_card and vals.get('note'):
                raise osv.except_osv(_('错误!'), u'卡券订单请内部备注字段里写备注！')
            if so_obj.is_card and not so_obj.real_so_id and not so_obj.sale_order_card_product_ids:
                raise osv.except_osv(_('错误!'),
                                     u'请添加订单卡券产品关联后再保存！')
            if so_obj.card_model_id:
                for line in so_obj.order_line:
                    if line.product_id.type != 'service' and not line.product_id.is_card :
                        raise osv.except_osv(_('Warning!'),
                                             u'卡券模型 相关的订单的产品不能为实物产品！')
            product_codes =[]
            # 自动生成的卡券制作产品,不能删除! 不然会影响到后续的一系列的操作.
            if so_obj.is_card and so_obj.state == 'draft':
                self.check_card_qty_equal_product_uom_qty(cr, uid, so_obj.id, context=context)
                for line in so_obj.order_line:
                    product_codes.append(line.product_id.default_code)
                if not so_obj.real_so_id and so_obj.name not in product_codes:
                    raise osv.except_osv(_('Warning!'),
                                         u'自动生成的卡券制作产品,不能删除!')
                product_suite_count = []
                [product_suite_count.append(product.suite)
                 for product in so_obj.sale_order_card_product_ids]
                if not so_obj.real_so_id and len(list(set(product_suite_count))) != so_obj.how_choice_one:
                    raise osv.except_osv(_('Warning!'),
                                         u'套件选项和几选一的数量不一致!')
            # else:   ##整体业务逻辑判断变更，取消这段功能
            #     compare_time = True
            #     #如果是中秋订单则不用比较发货日期
            #     for line in so_obj.order_line:
            #         seanson_list = [season.season for season in line.product_id.product_tmpl_id.season_ids]
            #         if len(seanson_list) != len(list(set(seanson_list) - set(['zhongqiu']))):
            #             compare_time = False
            #     #如果假单则不用比较发货日期
            #     if so_obj.real_so_id:
            #         compare_time = False
            #     # 卡券订单不用比
            #     if so_obj.order_type_id.id == card_exchange_order_id:
            #         compare_time = False
            #     if compare_time and vals.get('expected_delivery_time'):
            #         self.time_compare(cr, uid, so_obj.create_date, so_obj.expected_delivery_time, context=context)
        return sale_order_id

            # 需求变更 代码注释掉
            # if so_obj.order_type_id.id == card_exchange_order_id:
            #     product_ids = []
            #     # 1025 提货订单修改后保存需要校验订单行产品是否在卡券订单关联产品中，并且是否是同一个套件选项
            #     # ，否则报错：“产品选择不符合卡券订单的产品关联” [Issue开启]
            #     for line in so_obj.order_line:
            #         product_ids.append((line.product_id.id,line.product_uom_qty))
            #     product_suite_dict = {1:[],2:[],3:[],4:[],5:[],6:[],7:[],8:[],9:[],10:[]}
            #
            #     [(product_suite_dict.get(product.suite,[])).append((product.product_id.id,product.product_qty))
            #      for product in so_obj.card_so_id.sale_order_card_product_ids]
            #     suite_list_index = [set(product_ids) == set(product_list) for suite, product_list in product_suite_dict.items()]
            #     print suite_list_index
            #     if not any(suite_list_index):
            #         raise osv.except_osv(u'错误', u"产品或者数量与所选套餐不一致！")
            #     # so_obj.write({'suite':suite_list_index.index(True)+1})


    def create_sale_order_card_product(self, cr, uid, so_obj, context=None):
        model_data_obj = self.pool.get('ir.model.data')
        category_id = model_data_obj.xmlid_to_res_id(
            cr, SUPERUSER_ID, 'product.product_category_1')
        product_vals = {'name': u'卡券制作', 'sale_ok': False, 'purchase_ok': True, 'is_card': True,
                        'type': 'product', 'categ_id': category_id, 'standard_price': 1.5, 'list_price': 1.5,'active':False,
                        'default_code': '%s' % (so_obj.name)}
        product_id = model_data_obj.xmlid_to_res_id(
            cr, SUPERUSER_ID, 'dftg_ext.dftg_product_product_card_service_ext')
        order_line_id = self.pool.get('sale.order.line').search(cr,uid,[('order_id','=',so_obj.id),('product_id','=',product_id)],context=context)
        order_line = self.pool.get('sale.order.line').browse(cr,uid,order_line_id,context=context)

        product_tmpl_id = self.pool.get('product.template').create(cr, SUPERUSER_ID, product_vals, context=context)
        product_id = self.pool.get('product.product').search(cr,uid,[('product_tmpl_id','=',product_tmpl_id)],context=context)[0]
        return_values = self.pool.get('sale.order.line').product_id_change_with_wh(cr, uid, [so_obj.id], so_obj.pricelist_id.id,
                                                                    product_id,order_line.product_uom_qty, False, 0, False, '',so_obj.partner_id.id,
                                                                    False, True, so_obj.date_order, False, False,False, False,
                                                                                        context=context)
        (return_values.get('value')).update({'product_id': product_id, 'product_uom_qty': order_line.product_uom_qty, 'state': 'draft'})
        line_vals = [0, False, return_values.get('value')]
        return line_vals

    def on_change_many_choice_one(self, cr, uid, ids, how_choice_one, context=None):
        values = {}
        if not how_choice_one:
            values = {'how_choice_one': 1}
        return {'value': values}

    def on_change_how_choice_one(self, cr, uid, ids, how_choice_one, context=None):
        values = {}
        waring = {}
        if how_choice_one and how_choice_one % 1 != 0:
            waring = {'title': u'错误', 'message': u"填写的几选一数字必须为整数！"}
            values = {'how_choice_one': False}
        return {'value': values
            , 'warning': waring}

    def _compute_stock_reserved(self, cr, uid, ids, name, args, context=None):
        """#1026 销售订单增加一个计算字段“是否库存已保留”is_stock_reserved，监视销售订单的所有产品的
        已建立发货单数量等于产品数量，并且所有的出库单状态是等待移动或者完成状态 [Issue开启]"""
        res = {}
        for so_row in self.browse(cr, uid, ids, context=context):
            is_all_create_picking = True
            for sol_row in so_row.order_line:
                if sol_row.delivered_qty != abs(sol_row.product_uom_qty):
                    is_all_create_picking = False
            picking_ids = self.pool.get('stock.picking').search(cr, uid, [('so_id', '=', so_row.id),('state','!=','cancel')], context=context)
            is_all_reserve = []
            if is_all_create_picking:
                for picking_row in self.pool.get('stock.picking').browse(cr, uid, picking_ids, context=context):
                    if picking_row.state == 'assigned' or picking_row.state == 'done':

                        is_all_reserve.append(True)
                    else:
                        is_all_reserve.append(False)
            if len(is_all_reserve) > 0 and all(is_all_reserve):
                res[so_row.id] = True
            else:
                res[so_row.id] = False
        return res

    def _monitor_order_line_delivered_qty(self, cr, uid, ids, context=None):
        """#1026 销售订单增加一个计算字段“是否库存已保留”is_stock_reserved，监视销售订单的所有产品的
            已建立发货单数量等于产品数量，并且所有的出库单状态是等待移动或者完成状态 [Issue开启]"""
        res = {}
        for sol_row in self.browse(cr, uid, ids, context=context):
            res[sol_row.order_id.id] = True
        return res

    def _monitor_stock_picking_state(self, cr, uid, ids, context=None):
        """#1026 销售订单增加一个计算字段“是否库存已保留”is_stock_reserved，监视销售订单的所有产品的
            已建立发货单数量等于产品数量，并且所有的出库单状态是等待移动或者完成状态 [Issue开启]"""
        res = {}
        for picking_row in self.browse(cr, uid, ids, context=context):
            if picking_row.so_id:
                res[picking_row.so_id.id] = True
        return res.keys()

    def _get_pickings(self, cr, uid, ids, context=None):
        """#1026 销售订单增加一个计算字段“是否库存已保留”is_stock_reserved，监视销售订单的所有产品的
            已建立发货单数量等于产品数量，并且所有的出库单状态是等待移动或者完成状态 [Issue开启]"""
        res = set()
        for move in self.browse(cr, uid, ids, context=context):
            if move.picking_id:
                if move.picking_id.so_id:
                    res.add(move.picking_id.so_id.id)
        return list(res)

    #卡券物流成本合计
    def _card_logistics_cost_sum(self, cr, uid, ids, name, args, context=None):
        res = {}
        delivery_obj = self.pool.get('apply.delivery')
        for sale_order in self.browse(cr, uid, ids, context=context):
            delivery_ids = delivery_obj.search(cr, uid, [('card_so_id', '=', sale_order.id)], context=context)
            res[sale_order.id] = sum([delivery_row.delivery_cost or 0 for delivery_row in delivery_obj.browse(cr, uid, delivery_ids, context=context)] or [0])
        return res

    #计算剩余卡券数
    def compute_residue_card_qty(self, cr, uid, ids, name, args, context=None):
        res = {}
        for sale_order in self.browse(cr, uid, ids, context=context):
            exchange_sum = 0
            suite_exchange_count_map = {}
            for socp_row in sale_order.sale_order_card_product_ids:
                card_exchange_count = socp_row.card_exchange_count
                suite_exchange_count_map.update({socp_row.suite: card_exchange_count / socp_row.product_qty})

            for suite in suite_exchange_count_map:
                exchange_sum += suite_exchange_count_map.get(suite)
            #剩余份数
            res[sale_order.id] = sale_order.card_qty - exchange_sum
        return res

    def _get_onchange_count(self, cr, uid, ids, context=None):
        res ={}
        for socp in self.pool.get('sale.order.card.product').browse(cr, uid, ids, context=context):
            res[socp.order_id.id] = True
        return res.keys()

    def _get_sale_order_apply_delivery(self, cr, uid, ids, context=None):
        sale_ids = []
        for apply_row in self.browse(cr, uid, ids, context=context):
            sale_ids.append(apply_row.card_so_id.id)
        return sale_ids

    _columns = {
        'card_model_id': fields.many2one('card.model', string='卡券模型',copy=False),
        'order_type_id': fields.many2one('order.type', string="订单类型", copy=True),
        'is_card_exchange':fields.boolean(u'是否提货订单'),
        'is_multiple_choice_one':fields.boolean(u'是否多选一'),
        'sale_order_card_product_ids':fields.one2many('sale.order.card.product', 'order_id', string='卡券提货产品', copy=True),
        'card_exchange_start_date': fields.date(u'提货起始日期'),
        'card_exchange_end_date': fields.date(u'提货结束日期'),
        'card_exchange_margin_days': fields.float(u'预计发货预留天数',digits_compute=dp.get_precision('Product Unit of Measure')),
        'card_so_id': fields.many2one('sale.order', u'卡券订单', copy=True),
        'how_choice_one': fields.float(u'几选一', digits_compute=dp.get_precision('Product Unit of Measure')),
        'is_stock_reserved': fields.function(_compute_stock_reserved, string='是否库存已保留', type="boolean",store={
            'sale.order.line': (_monitor_order_line_delivered_qty, ['delivered_qty'], 30),
            'stock.picking':(_monitor_stock_picking_state, ['state'], 30),
            'stock.move': (_get_pickings, ['state', 'picking_id', 'partially_available'], 30),
        },copy=False),
        'express_delivery_ids':fields.one2many('sale.order.express.delivery','so_id',string='快递联系人地址',copy=False),
        'card_qty':fields.float(u'总提货份数', digits_compute=dp.get_precision('Product Unit of Measure')),
        'residue_card_qty': fields.function(compute_residue_card_qty, string=u'剩余提货份数', type='integer',
                                               store={
                                                   'sale.order': (lambda self, cr, uid, ids, c={}: ids, ['card_qty'], 10),
                                                   'sale.order.card.product': (_get_onchange_count, ['card_exchange_count'], 30)
                                               },
                                               copy=False),
        'card_product_id':fields.related('sale_order_card_product_ids','product_id',type='many2one',
                                         relation='product.product',string='卡券产品'),
        'card_express_type': fields.many2one('express.type', u'快递方式'),
        'apply_delivery_ids': fields.one2many('apply.delivery', 'card_so_id', u'卡券兑换记录', copy=False), # 需要子表one2many关联字段才能使store的子表变更触发主表的计算字段
        'card_logistics_cost': fields.function(_card_logistics_cost_sum, string=u'卡券物流成本', type='float', copy=False),

    }
    _defaults = {
        'how_choice_one': 1,
        'card_qty':1,
    }

class apply_export_wizard(osv.osv):
    _inherit = 'apply.export.wizard'

    _columns = {
        'import_message':fields.text(u'导出信息')
    }
    def model_export_data_header_field(self, cr, uid, selected, context=None):
        delivery_obj = self.pool.get('apply.delivery')
        field_names = ['copy_code', 'product_id', 'to_name', 'province_id', 'city_id', 'county_id', 'address',
                       'phone_number', 'mobile_number',
                       'estimated_ship_date','delivery_product_list', 'card_express_type', 'shipping_type', 'shipping_code',]

        # to_export_ids = delivery_obj.search(
        #     cr, uid, [('is_export', '=', False), ('id', 'in', selected)], context=context)

        #exported = len(context.get('active_ids')) - len(to_export_ids)
        to_export_ids = delivery_obj.search(cr, uid, [('id', 'in', selected)], order='delivery_product_list DESC', context=context)
        # 1054 卡券提货记录导出时需要检查is_stock_reserved是否为True，不是的话忽略导出 [Issue开启]
        # 如果未锁定或者已导出或者卡券订单已取消则不导出
        for delivery_row in delivery_obj.browse(cr, uid, to_export_ids, context=context):
            if not delivery_row.so_id.is_stock_reserved \
                    or delivery_row.card_so_id.state == 'cancel' \
                    or (delivery_row.is_export and uid != SUPERUSER_ID):
                to_export_ids.remove(delivery_row.id)
        exported_length = len(to_export_ids)
        header = [u"卡号", u"卡券种类", u"联系人", u"省", u"市", u"县", u"街道",
                  u"电话号码", u"手机号码", u"期望发货日期",u'发货内容', u'要求的快递方式', u'快递方式', u"快递单号"]
        length = len(header)
        export_data = delivery_obj.export_data(
            cr, uid, to_export_ids, [field_names[name] for name in xrange(length)], context=context).get('datas', [])
        so_ids = [delivery_row.so_id.id for delivery_row in delivery_obj.browse(cr,uid,to_export_ids,context=context)]

        picking_ids = self.pool.get('stock.picking').search(cr, uid, [('so_id', 'in', so_ids)],
                                                            context=context)
        self.pool.get('stock.picking').write(cr, uid, picking_ids, {'print_state': True}, context=context)
        now = datetime.now()
        #只写入未导出的记录, 防止覆盖导出时间和导出人
        first_export_ids = delivery_obj.search(cr, uid, [('id', 'in', to_export_ids),
                                                         ('is_export', '=', False),
                                                         ('is_shipping', '=', False)],
                                               order='delivery_product_list', context=context)
        delivery_obj.write(cr, uid, first_export_ids, {'is_export': True, 'export_date': now, 'export_uid': uid}, context=context)
        return header, export_data, exported_length, to_export_ids

    def return_vals_construction(self, cr, uid, ids, exported, this_id, out,to_export_ids, context=None):
        import_message = u"库存已锁定并且未导出过的记录有%s条, 请下载excel文件!" % (len(to_export_ids))
        if len(to_export_ids) == 0:
            import_message = u"没有库存已锁定并且未导出过的记录，请检查！"
        self.write(cr, uid, ids, {'import_message': import_message}, context=context)
        return super(apply_export_wizard, self).return_vals_construction(cr, uid, ids, exported, this_id, out,to_export_ids,
                                                                         context=context)


class apply_import_wizard(osv.osv):
    _inherit = 'apply.import.wizard'

    def header_data(self, cr, uid, ids, context=None):
        return [u"卡号", u"卡券种类", u"联系人", u"省", u"市", u"县", u"街道",
                u"电话号码", u"手机号码", u"期望发货日期", u'发货内容', u"要求的快递方式", u"快递方式", u"快递单号"]

    def picking_automatic_outgonging(self, cr, uid, picking_ids, context=None):
        ####################### 正常出货流程太慢
        #picking_obj = self.pool.get('stock.picking')
        # context.setdefault('is_stock_picking_out', True)
        # [picking_obj.do_enter_transfer_details(
        #     cr, uid, [picking_id], context=context) for picking_id in picking_ids]
        # transfer_details_ids = self.pool.get('stock.transfer_details').search(
        #     cr, uid, [('picking_id', 'in',picking_ids )], context=context)
        # self.pool.get('stock.transfer_details').do_detailed_transfer(
        #     cr, uid, transfer_details_ids, context=context)

        ###################### 尝试用直接sql修改库位和状态的方式
        #更新quant的库位和去除保留
        if picking_ids:
            cr.execute("SELECT quant.id as quant_id,  quant.reservation_id as move_id, move.location_dest_id as location_dest_id "
                       "FROM stock_quant quant "
                       "LEFT JOIN stock_move move on move.id=quant.reservation_id "
                       "WHERE quant.reservation_id IN (SELECT id FROM stock_move WHERE picking_id in %s)", (tuple(picking_ids),))
            quant_move_rel_rows = cr.dictfetchall()
            insert_quant_move_rel_sqls = ["INSERT INTO stock_quant_move_rel(quant_id, move_id) VALUES"]
            now = time.strftime(DEFAULT_SERVER_DATETIME_FORMAT)
            for quant_move_rel in quant_move_rel_rows:
                if len(insert_quant_move_rel_sqls) > 1:
                    insert_quant_move_rel_sqls.append(",")
                quant_id = quant_move_rel.get('quant_id')
                move_id = quant_move_rel.get('move_id')
                insert_quant_move_rel_sqls.append("(%s, %s)" % (quant_id, move_id))
                cr.execute("UPDATE stock_quant SET location_id=%s, reservation_id=NULL, write_date=%s "
                           "WHERE id=%s",
                           (quant_move_rel.get('location_dest_id'), now, quant_id))
            cr.execute(''.join(insert_quant_move_rel_sqls))

            #出库单行状态done
            cr.execute("UPDATE stock_move SET state='done', date=%s, write_date=%s WHERE id IN (SELECT id FROM "
                       "stock_move WHERE picking_id IN %s)", (now, now, tuple(picking_ids)))
            #出库单状态done
            cr.execute("UPDATE stock_picking SET state='done', date_done=%s, write_date=%s WHERE id IN %s",
                       (now, now, tuple(picking_ids)))
            #订单行的已出库单数设置为已建立出库单数
            cr.execute("UPDATE sale_order_line SET shipped_qty=delivered_qty, not_shipped_qty=FALSE,write_date=%s "
                       " WHERE order_id IN ( "
                        "SELECT so_id FROM stock_picking WHERE id IN %s)", (now, tuple(picking_ids)))
            #更改订单状态和已发货完成

            cr.execute("UPDATE sale_order SET shipment_completion=TRUE, state='done', write_date=%s "
                       " WHERE id IN ( "
                        "SELECT so_id FROM stock_picking WHERE id IN %s)", (now, tuple(picking_ids)))
        return True

    def automatic_outgonging(self, cr, uid, delivery_ids, context=None):
        """#1036 提货记录导入快递单号需要检查该提货记录对应的提货订单是否库存已保留（is_stock_reserved为True）
        ，自动调用次提货订单的所有出库单的移动按钮，成功后才能写入快递和物流单号和置为已发货状态 [Issue开启]"""
        delivery_obj = self.pool.get('apply.delivery')
        picking_obj = self.pool.get('stock.picking')
        is_stock_reserved_so_ids = [delivery.so_id.id for delivery in
                                    delivery_obj.browse(cr, uid, delivery_ids, context=context)
                                    if delivery.so_id.is_stock_reserved == True]
        all_so_ids = [delivery.so_id.id for delivery in delivery_obj.browse(cr, uid, delivery_ids, context=context)]
        if len(is_stock_reserved_so_ids) != len(delivery_ids):
            so_ids = list(set(all_so_ids) - set(is_stock_reserved_so_ids))
            raise osv.except_osv(_('Warning!'),
                                 u'导入的提货记录对应的提货订单(%s)产品没有保留成功，不能发货!' % (
                                     '、'.join([so_row.name for so_row in
                                               self.pool.get('sale.order').browse(cr, uid, so_ids, context=context)])
                                 ))

        picking_ids = picking_obj.search(cr, uid, [('so_id', 'in', all_so_ids),('state','=','assigned')], context=context)
        #批量出库操作
        self.picking_automatic_outgonging(cr,uid,picking_ids,context=context)
        picking_state_values= []
        self.pool.get('sale.order').card_order_create_account_move(cr, uid, all_so_ids, context=context)
        if picking_ids:
            cr.execute("SELECT * FROM stock_picking WHERE id IN %s", (tuple(picking_ids),))
            picking_rows = cr.dictfetchall()
            for picking_row in picking_rows:
                if picking_row.get('state') != 'done':
                    picking_state_values.append((picking_row.get('origin'), picking_row.get('name')))
            if picking_state_values:
                so_name = '、'.join([so_name[0] for so_name in  picking_state_values])
                picking_name = '、'.join([so_name[1] for so_name in picking_state_values])
                raise osv.except_osv(_('Warning!'),
                u'%s提货订单的，出库单%s出库失败，请核对后再进行操作!'%(so_name,picking_name))
        else:
            raise osv.except_osv(_('Warning!'), u'卡券%s提货记录，已经发货，不嫩重复导入 !' % delivery.copy_code)

class check_usable_lock_in_stock(osv.osv_memory):
    """ #1055 卡券提货页面增加两个过滤器“未锁库存”、“已锁库存”，
    更多菜单中添加一个“检查可用库存并锁定”向导，把选择的提货记录的提
    货订单进行逐条生成发货单并检查可用 [Issue开启]卡券提货记录颜色标注一下，已锁定库存的用绿色表示"""
    _name = 'check.usable.lock.in.stock'
    _columns = {
        'checked_record_num': fields.integer(u'已选则提货记录条数',readonly=True),
        'select_message':fields.text(u'反馈信息！'),
    }
    def set_having_picking_text(self,cr,uid,context=None):
        # if self.check_so_no_picking(cr,uid,context=context)[1]:
        #     str = "这部分提货订单需要手动进行操作！" # %(self.check_so_no_picking(cr,uid,context=context)[1])
        # else:
        #    str = '您选中的记录都正常, 请点击确认锁定库存按钮'
        return '您选中的记录都正常, 请点击确认锁定库存按钮'

    _defaults = {
        'checked_record_num': lambda  s,c,u,ctx =None:len(ctx.get('active_ids')),
        'select_message': set_having_picking_text,
    }

    #检查没有出库单的销售订单
    def check_so_no_picking(self, cr, uid, context=None):
        picking_obj = self.pool.get('stock.picking')
        delivery_obj = self.pool.get('apply.delivery')
        #提货记录对应的订单没有取消或者提货记录没有取消,才能进一步锁定
        so_ids = [delivery_row.so_id.id for delivery_row in
                  delivery_obj.browse(cr, uid, context.get('active_ids'), context=context)
                  if delivery_row.so_id.state not in ('cancel', 'sent') and not delivery_row.is_canceled]
        sale_obj = self.pool.get('sale.order')
        result_list, so_name_list, lock_ok_list = [], [], []
        for so_row in sale_obj.browse(cr, uid, so_ids, context=context):
            if so_row.state == 'draft': #个别特殊情况是会导致自动确认不成功。
                sale_obj.action_button_confirm(cr, uid, [so_row.id], context=context)
            picking_id = picking_obj.search(cr, uid, [('so_id', '=', so_row.id), ('state', 'not in', ('cancel', 'done'))],
                                                               order='id DESC', context=context)
            if not picking_id:
                result_list.append(so_row.id)
            else:
                # picking单标记为可用(确认)
                picking_rows = picking_obj.browse(cr, uid, picking_id, context=context)
                picking_assigin_state = []
                for picking_row in picking_rows:
                    if picking_row.state == "draft":
                        picking_obj.action_confirm(cr, uid, picking_id, context=context)
                    # picking单检查可用
                    if picking_row.state in ('waiting', 'confirmed'):
                        picking_obj.action_assign(cr, uid, picking_id, context)
                    if picking_row.state == 'assigned':
                        picking_assigin_state.append(True)
                    else:
                        picking_obj.do_unreserve(cr, uid, picking_row.id, context=context)
                        picking_assigin_state.append(False)
                    so_name_list.append(so_row.name)
                if all(picking_assigin_state):
                    lock_ok_list.append(so_row.name)
        return result_list, so_name_list, lock_ok_list

    #对卡券提货记录进行锁定库存操作
    def check_usable_lock_stock(self, cr, uid, ids, context=None):
        sale_obj = self.pool.get('sale.order')
        so_ids, so_name_list, lock_ok_list = self.check_so_no_picking(cr, uid, context=context)
        success_num = [sale_obj.card_locked_inventory_operation(cr, 1, so_id, context=context) for so_id in so_ids].count(True)
        return {
            'type': 'ir.actions.act_window',
            'res_model': "check.usable.lock.in.stock",
            'view_type': 'form',
            'view_mode': 'form',
            'target': 'new',
            'context': {'no_button': True,
                        'default_select_message': """操作成功！本次勾选%s条待处理记录，\n成功检查可用库存并锁定%s条"""%(
                                                    len(context.get('active_ids')), success_num + len(lock_ok_list))}}
    def success_button_comfirm(self,cr,uid,ids,context=None):
        return True



class import_delivery_cost_wizard(osv.osv):
    """#1037 卡券－提货导入快递单号下方增加一个菜单“导入提货快递费用”，约定excel的表头用“快递单号”和“费用”
    (现实excel文件由快递公司提供，肯定多于这2个字段)，在提货记录里找到对应的提货记录写入费用
    （要先在提货记录里增加“费用”delivery_cost字段） [置顶] [Issue开启]"""
    _name = 'import.delivery.cost.wizard'
    _inherit = ['import.wizard']
    _columns = {
        'feedback_message': fields.text(u'反馈信息！'),
        'state': fields.selection([('1', '1'), ('2', '2'), ('3', '3')], string=u'状态'),
    }
    def need_column_date_header(self, cr, uid, context=None):
        return [('express_code',u"快递单号"), ('delivery_cost',u"费用")]

    def return_vals_action(self, cr, uid, ids, this_id, context=None):
        return {
            'type': 'ir.actions.act_window',
            'res_model': 'import.delivery.cost.wizard',
            'view_mode': 'form',
            'view_type': 'form',
            'res_id': this_id,
            'views': [(False, 'form')],
            'target': 'new',
        }

    def judge_and_write_vals(self, cr, uid,ids, data_dcit, context=None):
        delivery_obj = self.pool.get('apply.delivery')
        delivery_ids = delivery_obj.search(cr, uid, [('shipping_code', '=', long(data_dcit.get('express_code')))], context=context)
        if delivery_ids:
            if len(delivery_ids)==1:
                try:
                    delivery_cost=float(data_dcit.get('delivery_cost'))
                except Exception:
                    raise osv.except_osv(u'错误', u'快递单号%s的费用不是数字'%(data_dcit.get('express_code')))
                delivery_obj.write(cr, uid, delivery_ids, {'delivery_cost': delivery_cost}, context=context)
            elif len(delivery_ids) > 1:
                raise osv.except_osv(u'错误', u'系统里存在重复的快递单号%s' % (data_dcit.get('express_code')))
        return delivery_ids and float(data_dcit.get('delivery_cost')) or False

    def button_ok(self, cr, uid, ids, context=None):
        cost_wizard = self.browse(cr, uid, ids, context=context)
        to_import = eval(cost_wizard.to_import)
        if not to_import:
            raise osv.except_osv(u'错误', u'文件中没有需要导入的行')
        all_cost = 0
        for data_dcit in to_import:
            all_cost += self.judge_and_write_vals(cr, uid, ids, data_dcit, context=context)
        this_id = self.create(cr, uid, {'state': '3',
                                        'feedback_message':"导入文件的总条数%s\n有效条数%s\n快递费用总额%s"
                                                           %(cost_wizard.selected,cost_wizard.imported,all_cost)}
                              , context=context)
        return self.return_vals_action(cr, uid, ids, this_id, context=context)